package com.sansan.qiangji.aspect;

import com.sansan.qiangji.constant.RedisConstant;
import com.sansan.qiangji.entity.system.Power;
import com.sansan.qiangji.entity.system.User;
import com.sansan.qiangji.enums.ResultEnum;
import com.sansan.qiangji.exception.ServiceException;
import com.sansan.qiangji.exception.UserAuthorizeException;
import com.sansan.qiangji.service.impl.system.PowerServiceImpl;
import com.sansan.qiangji.service.impl.system.UserServiceImpl;
import com.sansan.qiangji.utils.JWTUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
public class UserAuthorizeAspect {
    private StringRedisTemplate redisTemplate;

    private PowerServiceImpl powerService;

    private UserServiceImpl userService;

    @Autowired
    private void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    private void setPowerService(PowerServiceImpl powerService) {
        this.powerService = powerService;
    }

    @Autowired
    private void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Pointcut("execution(public * com.sansan.qiangji.controller.*.*.*(..))" +
            "&& !execution(public * com.sansan.qiangji.controller.open.OpenController.*(..))" +
            "&& !execution(public * com.sansan.qiangji.controller.system.UserController.userLogin(..))" +
            "&& !execution(public * com.sansan.qiangji.controller.pda.PdaController.userPdaLogin(..))")
    public void verify() {
    }

    @Before("verify()")
    public void doAuthorize() {
        log.info("【AOP】身份验证");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String accessToken = request.getHeader("accessToken");
        if (accessToken == null) {
            log.error("【AOP】accessToken为NULL");
            throw new UserAuthorizeException();
        }
        log.info("【AOP】accessToken: " + accessToken);
        String userInfo = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, accessToken));
        if (StringUtils.isEmpty(userInfo)) {
            log.error("【AOP】accessToken异常");
            throw new UserAuthorizeException();
        }
        Claims userInfoClaims = JWTUtil.checkJWT(userInfo);
        if (userInfoClaims == null) {
            log.error("【AOP】userInfo解密失败");
            throw new UserAuthorizeException();
        }
        String userName = userInfoClaims.get("userName", String.class);
        String loginTime = userInfoClaims.get("loginTime", String.class);
        String loginMethod = userInfoClaims.get("loginMethod", String.class);
        log.info("【AOP】用户：userName = {}, loginTime = {}, loginMethod = {}", userName, loginTime, loginMethod);
        request.setAttribute("userName", userName);
        request.setAttribute("loginTime", loginTime);
        request.setAttribute("loginMethod", loginMethod);
        User user = userService.findByUserName(userName);
        if (user == null || !user.getUserStatus()) {
            log.error("【AOP】用户状态异常");
            throw new UserAuthorizeException();
        }
        String requestPath = request.getServletPath();
        List<Power> powers = powerService.findAllPower();
        powers = powers.stream().filter(e -> e.getPowerPath().equals(requestPath)).collect(Collectors.toList());
        if (powers.size() != 0 && powers.get(0).getPowerStatus()) {
            List<String> powerAccessMethods = new ArrayList<>();
            if (powers.get(0).getPowerAccessMethod() != null) {
                powerAccessMethods = Arrays.asList(powers.get(0).getPowerAccessMethod().split("\\|\\|"));
            }
            if (powerAccessMethods.stream().noneMatch(e -> e.equals(loginMethod))) {
                log.error("【AOP】{}接口没有{}权限", powers.get(0).getPowerName(), loginMethod);
                throw new ServiceException(ResultEnum.LOGIN_METHOD_NOT_HAVE_POWER_ACCESS_METHODS.getCode(), powers.get(0).getPowerName()+" 接口没有  " + loginMethod + " 权限!");
            }
            List<String> userPowers = new ArrayList<>();
            if (user.getUserPowers() != null) {
                userPowers = Arrays.asList(user.getUserPowers().split("\\|\\|"));
            }
            List<Power> finalPowers = powers;
            if (userPowers.stream().noneMatch(e -> e.equals(finalPowers.get(0).getPowerName()))) {
                log.error("【AOP】{}没有{}权限", userName, finalPowers.get(0).getPowerName());
                throw new ServiceException(ResultEnum.USER_NOT_HAVE_POWER.getCode(), "您没有 " + finalPowers.get(0).getPowerName() + " 权限，请与管理员联系!");
            }
        }
    }
}
